/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package milkyway.hightech.banking;

import milkyway.hightech.exceptions.TransactionNotAllowedException;
import milkyway.hightech.money.Currency;
import milkyway.hightech.money.Money;
import milkyway.hightech.timer.TimeEmitter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.Stack;
import java.util.logging.Logger;
import milkyway.hightech.entry.Entry;

/**
 *
 * @author michael
 */
public abstract class Account extends Observable implements Observer
{
    //variables
    
    /**
     * The account number to identify the account
     */
    private final int accountNumber;

    /**
     * the surname and lastname of the account holder
     */
    private String accountHolder;

    /**
     * the current account balance
     */
    protected Money money;

    /**
     *
     */
    private List<Entry> list = new Stack<Entry>();
    
    /**
     * 
     */
    protected Calendar calendar;
    
    private static final Logger logger = Logger.getLogger(Account.class.getName());

    //end of variables

    /**
     * 
     * @param accountNumber
     * @param accountHolder
     * @param currency
     * @param calendar 
     */
    public Account(int accountNumber, String accountHolder, Currency currency, Calendar calendar)
    {
	this.accountNumber = accountNumber;
	this.accountHolder = accountHolder;
	this.money = new Money(new BigInteger("0"), currency);
	this.calendar = calendar;
	
	TimeEmitter.getInstance().addObserver(this);
    }

    //interfaces

    /**
     * enters the given amount on credit
     * @param amount the amount of money which should be entered on credit
     * @param reasonOfTransfer the description of this transaction
     */
    protected void enterOnCredit(Money amount, String reasonOfTransfer)
    {
	switch(amount.getValue().signum())
	{
	    case -1:
	    case 0:
		throw new TransactionNotAllowedException(accountNumber + ", on "
			+ TimeEmitter.getInstance().getCalendar().getTime()
			+ ", could not be applied");
	    case 1:
		update(1, amount, reasonOfTransfer);
	}
    }

    /**
     * enters the given amount on debit
     * @param amount the amount of money which should be entered on debid
     * @param reasonOfTransfer the description of this transaction
     */
    protected void enterOnDebit(Money amount, String reasonOfTransfer)
    {
	switch(amount.getValue().signum())
	{
	    case -1:
	    case 0:
		throw new TransactionNotAllowedException(accountNumber + ", on "
			+ TimeEmitter.getInstance().getCalendar().getTime()
			+ ", could not be applied");
	    case 1:
		update(-1, amount, reasonOfTransfer);
		break;
	}   
    }

    /**
     * updates the account balance
     * @param amount the amount of money which should be entered on debid/credit
     * @param reasonOfTransfer the description of this transaction
     */
    protected void update(int typeEnter, Money amount, String reasonOfTransfer)
    {
	char sign = '+';
	
	if(amount.getCurrency() != money.getCurrency())
	    amount = money.convert(amount);
	
	if(amount.getValue().compareTo(new BigInteger("100000000")) >= 0)
	{
	    String s = accountNumber + ": high amount was transfered\n";
	    logger.info(s);
	}
	
	switch(typeEnter)
	{
	    case -1:
		money = money.subtract(amount);
		sign = '-';
		break;
	    case 1:
		money = money.add(amount);
		break;
	    default:
		throw new TypeNotPresentException(Integer.toString(typeEnter), null);
	}
	
	list.add(new Entry(reasonOfTransfer, sign, amount));
    }

    /**
     * pays interests to the account balance
     */
    protected abstract void payInterest();

    /**
     * returns the account number
     * @return the account number
     */
    public int getAccountNumber()
    {
	return accountNumber;
    }

    /**
     * returns the name of the account holder
     * @return the account holder
     */
    public String getAccountHolder()
    {
	return accountHolder;
    }

    /**
     * returns the current account balance
     * @return the current account balance
     */
    public BigInteger getAccountBalance()
    {
	return money.getValue();
    }
    
    public Currency getCurrency()
    {
	return money.getCurrency();
    }

    /**
     * 
     * @return the head of the account statement
     */
    @Override
    public String toString()
    {
	BigDecimal d = new BigDecimal(money.getValue().toString()).divide(BigDecimal.valueOf(100));
	
	String data = "Account holder: " + accountHolder
		+ "\nAccount number: " + accountNumber
		+ "\nAccount balance: " + d + " " + money.getCurrency();
	
	return data;
    }

    /**
     * 
     * @param c the comparator to sort by
     * @return the complete account statement
     */
    public String getAccountStatement(Comparator c)
    {
	String entrys = toString() + "\n\n";
	Collections.sort(list, c);
	Iterator iterator = list.iterator();
	
	while(iterator.hasNext())
	    entrys += iterator.next().toString();

	return entrys;
    }

    //End of Interfaces
}
